package com.zijidelu.luframework.utils.lang.reflect.bean;

import org.apache.commons.lang3.function.TriFunction;

import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * s
 * 动态构造器构建器。
 *
 * @author ZIJIDELU
 * @datetime 2025/11/28 12:45
 */
public class DynamicConstructorBuilder<T> {
    private final MethodHandles.Lookup privateLookup;
    private final MethodHandle constructorHandle;
    private final Class<T> objClass;
    private final Class<?>[] paramTypes;

    @SuppressWarnings("unchecked")
    private DynamicConstructorBuilder(Constructor<?> constructor) {
        // 目标类型
        this.objClass = (Class<T>) constructor.getDeclaringClass();
        // 参数类型
        this.paramTypes = constructor.getParameterTypes();

        // 查找当前类的Lookup
        final MethodHandles.Lookup lookup = MethodHandles.lookup();
        // 计算私有Lookup，用于访问构造器
        this.privateLookup = calcPrivateLookup(lookup, objClass);

        // 计算构造器的MethodHandle
        this.constructorHandle = calcConstructorHandle(privateLookup, objClass, paramTypes);
    }

    public static <T> DynamicConstructorBuilder<T> of(Constructor<T> constructor) {
        // 检查构造器是否兼容
        if (!isCompatible(constructor)) {
            throw new IllegalArgumentException("Constructor is not compatible: " + constructor);
        }
        return new DynamicConstructorBuilder<>(constructor);
    }

    /**
     * 判断构造器是否兼容
     *
     * @param constructor 构造器
     * @return 是否兼容
     */
    public static boolean isCompatible(Constructor<?> constructor) {
        Class<?>[] parameterTypes = constructor.getParameterTypes();
        int parameterCount = constructor.getParameterCount();
        // 校验构造器参数数量是否支持
        if (!(parameterCount >= 0 && parameterCount <= 3)) {
            return false;
        }

        for (Class<?> parameterType : parameterTypes) {
            // 不支持原始类型参数
            if (parameterType.isPrimitive()) {
                return false;
            }
        }

        return true;
    }

    /**
     * 计算私有Lookup，用于访问构造器
     *
     * @return 私有Lookup
     */
    public static MethodHandles.Lookup calcPrivateLookup(MethodHandles.Lookup lookup, Class<?> objClass) {
        try {
            return MethodHandles.privateLookupIn(objClass, lookup);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 计算构造器的MethodHandle
     *
     * @return 构造器的MethodHandle
     */
    public static MethodHandle calcConstructorHandle(MethodHandles.Lookup privateLookup, Class<?> objClass, Class<?>[] paramTypes) {
        try {
            return privateLookup.findConstructor(
                    objClass,
                    MethodType.methodType(void.class, paramTypes)
            );
        } catch (NoSuchMethodException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 为0参数构造器创建一个 Supplier 调用器。
     * 调用 get() 方法会创建一个新的实例。
     */
    @SuppressWarnings("unchecked")
    public Supplier<T> toConstructorInvoker0() {
        final CallSite site;
        try {
            site = LambdaMetafactory.metafactory(
                    privateLookup,
                    "get", // 关键点1: Supplier 的方法名是 "get"
                    MethodType.methodType(Supplier.class), // 关键点2: 目标接口是 Supplier
                    // 关键点3: Supplier.get() 的擦除签名，无参数，返回 Object
                    MethodType.methodType(Object.class),
                    constructorHandle, // 0参数构造器的句柄，类型为 ()void
                    // 关键点4: 动态调用签名，无参数，返回具体类型 T
                    MethodType.methodType(objClass)
            );
        } catch (LambdaConversionException e) {
            throw new RuntimeException(e);
        }

        final Supplier<T> constructorBuilder;
        try {
            constructorBuilder = (Supplier<T>) site.getTarget().invoke();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        return constructorBuilder;
    }

    /**
     * 创建1个参数的构造器调用器
     *
     * @return 构造器调用器
     */
    @SuppressWarnings("unchecked")
    public Function<Object, T> toConstructorInvoker1() {
        final CallSite site;
        try {
            site = LambdaMetafactory.metafactory(
                    privateLookup,
                    "apply",
                    MethodType.methodType(Function.class),
                    MethodType.methodType(Object.class, Object.class),
                    constructorHandle,
                    MethodType.methodType(objClass, paramTypes)
            );
        } catch (LambdaConversionException e) {
            throw new RuntimeException(e);
        }

        final Function<Object, T> constructorBuilder;
        try {
            constructorBuilder = (Function<Object, T>) site.getTarget().invoke();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        return constructorBuilder;
    }

    /**
     * 创建 2 个参数的构造器调用器
     *
     * @return 2 个参数的构造器调用器
     */
    @SuppressWarnings("unchecked")
    public BiFunction<Object, Object, T> toConstructorInvoker2() {
        final CallSite site;
        try {
            site = LambdaMetafactory.metafactory(
                    privateLookup,
                    "apply",
                    MethodType.methodType(BiFunction.class),
                    MethodType.methodType(Object.class, Object.class, Object.class),
                    constructorHandle,
                    MethodType.methodType(objClass, paramTypes)
            );
        } catch (LambdaConversionException e) {
            throw new RuntimeException(e);
        }

        final BiFunction<Object, Object, T> constructorBuilder;
        try {
            constructorBuilder = (BiFunction<Object, Object, T>) site.getTarget().invoke();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        return constructorBuilder;
    }

    /**
     * 创建 3 个参数的构造器调用器
     *
     * @return 3 个参数的构造器调用器
     */
    @SuppressWarnings("unchecked")
    public TriFunction<Object, Object, Object, T> toConstructorInvoker3() {
        final CallSite site;
        try {
            site = LambdaMetafactory.metafactory(
                    privateLookup,
                    "apply",
                    MethodType.methodType(TriFunction.class),
                    MethodType.methodType(Object.class, Object.class, Object.class, Object.class),
                    constructorHandle,
                    MethodType.methodType(objClass, paramTypes)
            );
        } catch (LambdaConversionException e) {
            throw new RuntimeException(e);
        }

        final TriFunction<Object, Object, Object, T> constructorBuilder;
        try {
            constructorBuilder = (TriFunction<Object, Object, Object, T>) site.getTarget().invoke();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        return constructorBuilder;
    }
}
